
#include "cgeSocket.h"
#define CGE_PLATFORM 1
#define CGE_LINUX 1
#include <sys/errno.h>

namespace basterd
{
	namespace net
	{
		// Some documentation
		//
		// Windows Sockets:
		// - recv() function, http://msdn.microsoft.com/en-us/library/ms740121(VS.85).aspx;
		// - connect() function, http://msdn.microsoft.com/en-us/library/ms740121(VS.85).aspx;
		// - closesocket() function, http://msdn.microsoft.com/en-us/library/ms740121(VS.85).aspx;
		// - send() function, http://msdn.microsoft.com/en-us/library/ms740149(VS.85).aspx
		// - bind() function, http://msdn.microsoft.com/en-us/library/ms737550(VS.85).aspx
		// - shutdown(), http://msdn.microsoft.com/en-us/library/ms740481.aspx
		// ----------------------------------------------------------------------------
		// Socket
		// ----------------------------------------------------------------------------
		Socket::Socket(void)
		    : hostAddress("127.0.0.1"), address(), socketId(0), port(0), connected(false)
		{
			memset( &address, 0, sizeof(address));

			create();
		}
		// ----------------------------------------------------------------------------
		Socket::Socket(__socket sid, __socketAddress& sad )
			: hostAddress("127.0.0.1"), address(), socketId(sid), port(0), connected(true)
		{
			memset( &address, 0, sizeof(address));

		#if CGE_PLATFORM == CGE_WIN32
			// these call aren't supported by Windows directly
			Engine::getInstance();

			hostAddress = inet_ntoa( sad.sin_addr );
			port        = ntohs(sad.sin_port);
			memcpy( &address, &sad, sizeof(__socketAddress ) );

		#elif CGE_PLATFORM == CGE_LINUX

			hostAddress = inet_ntoa( sad.sin_addr );
			port        = ntohs(sad.sin_port);
			memcpy( &address, &sad, sizeof(__socketAddress) );
		#endif

			connected = true;
		}
		// ----------------------------------------------------------------------------
		Socket::Socket(const String& hosta, unsigned short porta)
			: hostAddress(hosta), address(), socketId(0), port(porta), connected(false)
        {
			memset( &address, 0, sizeof(address));

			create();
			connect(hostAddress, port);
		}
		// ----------------------------------------------------------------------------
		Socket::~Socket(void)
		{
		#if   CGE_PLATFORM == CGE_WIN32
			::shutdown(socketId, SD_BOTH); // SD_BOTH = 2
		#elif CGE_PLATFORM == CGE_LINUX
			::shutdown(socketId,2);
		#endif

			close();
		}
		// ----------------------------------------------------------------------------		
		bool Socket::isConnected(void) const
		{
			// TODO: verificar se o socket está conectado.
			return connected;
		}
		// ----------------------------------------------------------------------------
		String Socket::getPeerName(void) const
		{
		#if   CGE_PLATFORM == CGE_WIN32
			sockaddr_in peer;
			int peer_len;

			if( getpeername(socketId, (sockaddr*) &peer, &peer_len) != -1)
			{
				StringStream ss;
				ss << inet_ntoa(peer.sin_addr) << ":" << ntohs(peer.sin_port);
				return ss.str();
			}

			return StringUtils::BLANK;
			
		#elif   CGE_PLATFORM == CGE_LINUX
		    sockaddr_in peer;
			int peer_len;

			if( getpeername(socketId, (sockaddr*) &peer, (socklen_t*) &peer_len) != -1)
			{
				//StringStream ss;
				std::stringstream ss;
				ss << inet_ntoa(peer.sin_addr) << ":" << ntohs(peer.sin_port);
				return ss.str();
			}

			return "";
			
		#endif
		}
		// ----------------------------------------------------------------------------
		String Socket::getHostName(void) const
		{
			char name[256];
			if( gethostname(name, 256) != -1)
			{
				return name;
			}

			return "";
		}
		// ----------------------------------------------------------------------------
		bool Socket::connect(const String& _host, unsigned short _port)
		{
			hostAddress = _host;
			port = _port;
			

		#if CGE_PLATFORM == CGE_WIN32
			// ----------------------------------------------------
			// windows implementation
			address.sin_family = AF_INET;
			address.sin_addr.s_addr = inet_addr(hostAddress.c_str());
			address.sin_port = htons(port);

			int status = ::connect(socketId, (SOCKADDR*) &address, sizeof(address));

			std::cout << "NOTE: status is " << status << std::endl;
			std::cout << "NOTE: error code is " << WSAGetLastError() << std::endl;
			if (status == SOCKET_ERROR)
			{	
				return connected = false;
			}

			return connected = true;

		#elif CGE_PLATFORM == CGE_LINUX
			// ----------------------------------------------------
			// linux code
			address.sin_family = AF_INET;
			address.sin_addr.s_addr = inet_addr(hostAddress.c_str());
			address.sin_port = htons (port);

			int status = ::connect (socketId, (sockaddr *) &address, sizeof (address));

			if (status != 0)
			{
				CGE_ERRF("Cannot connect: error code is " << status << " and socket ID is " <<  socketId );

				std::cerr << "error number: " << errno << std::endl<< std::endl;

				switch( errno )
				{
				case EACCES: CGE_ERRF("Permission to create a socket of the specified type and/or protocol is denied."); break;
				case EAFNOSUPPORT: CGE_ERRF("The implementation does not support the specified address family."); break;
				case EINVAL:
					CGE_ERRF("Unknown protocol, or protocol family not available OR invalid flags in type."); break;
				case EMFILE:
					CGE_ERRF("Process file table overflow."); break;
				case ENFILE:
					CGE_ERRF("The system limit on the total number of open files has been reached."); break;
				case ENOBUFS:
				case ENOMEM:
					CGE_ERRF("Insufficient memory is available. The socket cannot be created until sufficient resources are freed."); break;
				case EPROTONOSUPPORT:
					CGE_ERRF("The protocol type or the specified protocol is not supported within this domain.\n"); break;
				case ECONNREFUSED:
					CGE_ERRF("Connection refused\n"); break;
				default:
					CGE_ERRF("Unknown error.\n"); break;
				}
				return connected = false;
			}	

			return connected = true;
			
		#endif
		}
		// ----------------------------------------------------------------------------
		void Socket::close(void) const
		{
			if( connected )
			{
			#if CGE_PLATFORM == CGE_WIN32
			    // ----------------------------------------------------
			    // windows implementation
				::shutdown( socketId, 2 );
			    ::closesocket(socketId);
                
			#elif CGE_PLATFORM == CGE_LINUX
			    // ----------------------------------------------------
			    // linux implementation
				::shutdown( socketId, 2 );
			    ::close(socketId);
			    
			#endif
			}
		}
		// ----------------------------------------------------------------------------
		int Socket::send( const void* data, size_t sizeInBytes ) const
		{
	    #if CGE_PLATFORM == CGE_WIN32
            // ----------------------------------------------------
			// windows implementation
            return ::send(socketId, (const char*) data, (int)sizeInBytes, 0);
                
		#elif CGE_PLATFORM == CGE_LINUX
		    // ----------------------------------------------------
			// linux implementation
		    return ::send(socketId, data, sizeInBytes, MSG_NOSIGNAL);
		    
		#endif
		}
		// ----------------------------------------------------------------------------
		int Socket::receive( void* buff, size_t sizeInBytes ) const
		{
		#if CGE_PLATFORM == CGE_WIN32
            		// ----------------------------------------------------
			// windows implementation
			memset(buff, 0, sizeInBytes);
			int status = ::recv ( socketId, (char*) buff, (int)sizeInBytes, 0 );

			if( status == SOCKET_ERROR )
			{
				// receive closed:
				//  - all data was received, and the connection was gracefully closed.
				int errorCode = WSAGetLastError();

				return 0;
			}

			if (status == 0)
			{
		                // receive closed:
				//  - all data was received, and the connection was gracefully closed.
				connected = false;
                		return status;
                	} else if (status < 0) {
                		//ERROR
				return status;
			}

			return status;
			
		#elif CGE_PLATFORM == CGE_LINUX
			// ----------------------------------------------------
			// linux implementation
			memset(buff, 0, sizeInBytes);
			int status = ::recv( socketId, buff, sizeInBytes, 0 );
			
			if (status < 1)
			{
				connected = false;
				return status;
			}

			return status;
            
        #endif
		}
		// ----------------------------------------------------------------------------
		bool Socket::shutdown(__socket theSocket, int how)
		{
		#if CGE_PLATFORM == CGE_WIN32
            // ----------------------------------------------------
			// windows implementation
			return ( !(::shutdown(theSocket, how)) );
	    #elif CGE_PLATFORM == CGE_LINUX
	        // ----------------------------------------------------
			// linux implementation
			return ( !(::shutdown(theSocket, how)) );
		#endif
		}
		// ----------------------------------------------------------------------------
		unsigned short Socket::getPort(void) const
		{
			return port;
		}
		// ----------------------------------------------------------------------------
		const String& Socket::getHostAddress(void) const
		{
			return hostAddress;
		}
		// ----------------------------------------------------------------------------
		bool Socket::create(void)
		{
		#if CGE_PLATFORM == CGE_WIN32
			// ----------------------------------------------------
			// windows implementation
			memset( &address,0, sizeof(__socketAddress) );
			socketId = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

			if ( socketId == INVALID_SOCKET )
			{
				return false;
			}
			
			return true;

		#elif CGE_PLATFORM == CGE_LINUX
			// ----------------------------------------------------
			// linux implementation
			memset( &address, 0, sizeof(__socketAddress) );

			socketId = ::socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
			//socketId = ::socket( AF_UNIX, SOCK_STREAM, IPPROTO_TCP );

			if ( socketId == -1 ) 
			{
				// ERROR	
				CGE_ERRF("Cannot create socket");
				return false;	
			}

			int on = 1;
			if ( ::setsockopt(socketId, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)) == -1 )
			{
				// ERROR
				CGE_ERRF("cannot set socket option");
				return false;
			}
			
			return true;

		#endif
		}
		// ----------------------------------------------------------------------------
		bool Socket::bind(const int port)
		{
        #if CGE_PLATFORM == CGE_WIN32
            // ----------------------------------------------------
			// windows implementation
            address.sin_family = AF_INET;
			address.sin_addr.s_addr = ::inet_addr(hostAddress.c_str());
            address.sin_port = htons(port);

            int bind_return = ::bind( socketId, (SOCKADDR*) &address, sizeof(address) );

            if ( bind_return == SOCKET_ERROR ) {
				// NOTE: close or not to close?
                // close();
                return false;
            }
            
        #elif CGE_PLATFORM == CGE_LINUX
            // ----------------------------------------------------
			// linux implementation
			address.sin_family = AF_INET;
            address.sin_addr.s_addr = inet_addr(hostAddress.c_str());
            address.sin_port = htons(port);

            int bind_return = ::bind( socketId, (struct sockaddr*) &address, sizeof(address) );

            if (bind_return == -1)
            {
            	std::cerr << "CANNOT BIND: " << errno << std::endl<< std::endl;

                return false;
            }
			
        #endif

			return true;
		}
		// ----------------------------------------------------------------------------
		int Socket::receive( String& str ) const
		{
			int lastStatus;
			char c;
			str = "";

			while( (lastStatus=receive( &c, 1 )) > 0 )
			{
				if( c == 0 )
				{					
					return (int)(1 + str.length());
				}else
				{
					str += c;
				}
			}

			return lastStatus;
		}
		// ----------------------------------------------------------------------------
		int Socket::send( const String& str ) const
		{
			return this->send( (void*)str.c_str(), (size_t) str.length()+1 );
		}
		// ----------------------------------------------------------------------------
	}
}
